#!/usr/bin/env python3
# app.py - 修复版 AI面试系统后端
from flask import Flask, request, jsonify, send_file
from flask_cors import CORS
from flask_socketio import SocketIO, emit, join_room, leave_room
import os
import base64
import threading
import time
import uuid
import json
import logging
from werkzeug.utils import secure_filename
from datetime import datetime

# 导入模块
from ocr import get_resume_text
from identity import extract_face_from_resume, verify_identity_web
from spark_chat import SparkWs
from rtasr_client import RtAsrClient
from tts import synthesize_and_play, get_audio_file_path
from emotion_utils import start_emotion_recording, stop_emotion_recording, get_emotion_stats, clear_emotion_data
from config import *

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

app = Flask(__name__)
app.config['SECRET_KEY'] = 'ai-interview-secret-key-2024'
app.config['UPLOAD_FOLDER'] = 'uploads'
CORS(app, origins="*")

# 🔥 修复：使用threading模式（最兼容）
socketio = SocketIO(app, cors_allowed_origins="*", async_mode='threading', logger=False, engineio_logger=False)

# 面试配置
ANSWER_TIME = 60  # 每题回答时间（秒）
TOTAL_ROUNDS = 3  # 面试轮次
EMOTION_SAMPLE_RATE = 1  # 情绪采样间隔（秒）

# 全局面试会话
interview_sessions = {}


class InterviewSession:
    """简化的面试会话类"""

    def __init__(self, session_id, job_position, resume_path):
        self.session_id = session_id
        self.job_position = job_position
        self.resume_path = resume_path
        self.resume_text = ""

        # 面试状态
        self.current_round = 0
        self.max_rounds = TOTAL_ROUNDS
        self.interview_started = False
        self.interview_completed = False
        self.is_identity_verified = False

        # 面试数据
        self.questions = []  # 预生成的所有题目
        self.answers = []  # 用户答案记录
        self.scores = {}  # 各项得分

        # 技术组件
        self.chatbot = None
        self.rtasr_client = None
        self.timer_active = False
        self.recording_active = False

        # 评分数据
        self.emotion_data = []
        self.answer_completeness = []
        self.fluency_scores = []

    def generate_questions(self):
        """预生成所有面试题目"""
        try:
            logger.info(f"为{self.job_position}岗位生成面试题目...")

            # 初始化AI面试官
            system_prompt = f"""你是{self.job_position}的专业面试官。
根据简历内容，生成{self.max_rounds}个递进式面试问题。
要求：
1. 第1题：自我介绍和基础背景
2. 第2题：专业技能和项目经验  
3. 第3题：职业规划和岗位匹配

请直接返回问题，不要其他内容。每个问题单独一行。"""

            self.chatbot = SparkWs(
                SPARK_APPID, SPARK_APIKey, SPARK_APISecret,
                SPARK_URL, SPARK_DOMAIN, system_prompt
            )

            # 生成题目
            prompt = f"简历内容：{self.resume_text[:500]}...\n\n请生成{self.max_rounds}个面试问题："
            response = self.chatbot.send_prompt(prompt)

            # 解析题目
            questions = [q.strip() for q in response.split('\n') if q.strip() and len(q.strip()) > 10]

            if len(questions) >= self.max_rounds:
                self.questions = questions[:self.max_rounds]
            else:
                # 备用题目
                fallback_questions = [
                    f"请简单介绍一下您自己，包括教育背景和{self.job_position}相关经验。",
                    f"请详细描述您在{self.job_position}方面最有成就感的一个项目或工作经历。",
                    f"您对{self.job_position}这个岗位有什么期望？未来3年的职业规划是什么？"
                ]
                self.questions = questions + fallback_questions[len(questions):self.max_rounds]

            logger.info(f"题目生成完成：{len(self.questions)}个问题")
            return True

        except Exception as e:
            logger.error(f"题目生成失败：{e}")
            # 使用默认题目
            self.questions = [
                f"请简单介绍一下您自己，包括教育背景和{self.job_position}相关经验。",
                f"请详细描述您在{self.job_position}方面最有成就感的一个项目或工作经历。",
                f"您对{self.job_position}这个岗位有什么期望？未来3年的职业规划是什么？"
            ]
            return False

    def init_asr(self):
        """初始化语音识别"""
        try:
            logger.info(f"初始化语音识别，APPID: {RTASR_APPID}")
            self.rtasr_client = RtAsrClient(RTASR_APPID, RTASR_APIKey)

            max_retries = 3
            for attempt in range(max_retries):
                if self.rtasr_client.connect():
                    logger.info(f"✅ 语音识别客户端连接成功 (尝试 {attempt + 1})")
                    return True
                else:
                    logger.warning(f"❌ 语音识别连接失败 (尝试 {attempt + 1}/{max_retries})")
                    if attempt < max_retries - 1:
                        time.sleep(2)

            logger.warning("语音识别客户端连接失败，但继续执行")
            return False
        except Exception as e:
            logger.error(f"语音识别初始化失败：{e}")
            return False

    def start_round(self, round_num):
        """开始新一轮面试"""
        if round_num > len(self.questions):
            return False

        self.current_round = round_num
        question = self.questions[round_num - 1]

        logger.info(f"开始第{round_num}轮面试：{question}")

        # 发送问题到前端
        socketio.emit('question_start', {
            'round': round_num,
            'question': question,
            'total_rounds': self.max_rounds
        }, room=self.session_id)

        # 播放语音
        self.play_question_audio(question)

        return True

    def play_question_audio(self, question):
        """播放问题语音"""

        def audio_thread():
            try:
                logger.info("🔊 开始语音合成...")
                success = synthesize_and_play(question)

                socketio.emit('audio_finished', {'success': success}, room=self.session_id)

                # 🔥 修复：直接调用计时器
                self.start_answer_timer()

            except Exception as e:
                logger.error(f"语音播放异常：{e}")
                socketio.emit('audio_finished', {'success': False}, room=self.session_id)
                self.start_answer_timer()

        # 使用线程处理音频
        thread = threading.Thread(target=audio_thread, daemon=True)
        thread.start()

    def start_answer_timer(self):
        """开始回答计时器"""
        logger.info(f"开始{ANSWER_TIME}秒回答计时")

        # 清理旧数据
        clear_emotion_data()

        # 开始录音和情绪监测
        self.recording_active = True
        self.timer_active = True
        start_emotion_recording()

        if self.rtasr_client:
            self.rtasr_client.clear_result()

        # 通知前端开始计时
        socketio.emit('timer_start', {
            'duration': ANSWER_TIME,
            'round': self.current_round
        }, room=self.session_id)

        # 🔥 修复：使用简单的线程计时器
        def timer_worker():
            # 启动录音
            if self.rtasr_client and self.rtasr_client.is_connected():
                try:
                    recording_thread = threading.Thread(
                        target=lambda: self.rtasr_client.record_and_send(ANSWER_TIME),
                        daemon=True
                    )
                    recording_thread.start()
                except Exception as e:
                    logger.error(f"录音启动失败：{e}")

            # 计时循环
            for remaining in range(ANSWER_TIME, 0, -1):
                if not self.timer_active or not self.recording_active:
                    logger.info(f"计时器被停止，剩余时间：{remaining}秒")
                    break

                socketio.emit('timer_update', {
                    'remaining': remaining,
                    'round': self.current_round
                }, room=self.session_id)

                time.sleep(1)

            # 时间到，结束回答
            if self.timer_active and self.recording_active:
                logger.info("⏰ 计时器到时，自动结束回答")
                self.end_answer_timer()

        # 启动计时器线程
        timer_thread = threading.Thread(target=timer_worker, daemon=True)
        timer_thread.start()

    def end_answer_timer(self):
        """结束回答计时"""
        if not self.recording_active:
            logger.info("计时器已经停止，跳过重复执行")
            return

        self.timer_active = False
        self.recording_active = False
        logger.info(f"第{self.current_round}轮回答时间结束")

        # 停止录音和情绪监测
        stop_emotion_recording()

        # 通知前端时间结束
        socketio.emit('timer_end', {
            'round': self.current_round,
            'message': '回答时间结束'
        }, room=self.session_id)

        # 获取用户答案
        user_answer = ""
        if self.rtasr_client:
            user_answer = self.rtasr_client.get_result()

        if not user_answer.strip():
            user_answer = "用户未回答或语音识别失败"

        # 打印语音识别结果供验证
        logger.info("=" * 60)
        logger.info(f"🎤 第{self.current_round}轮 用户语音识别结果:")
        logger.info(f"问题: {self.questions[self.current_round - 1]}")
        logger.info(f"回答: {user_answer}")
        logger.info(f"回答长度: {len(user_answer)} 字符")
        logger.info("=" * 60)

        # 记录答案
        answer_record = {
            'round': self.current_round,
            'question': self.questions[self.current_round - 1],
            'answer': user_answer,
            'timestamp': datetime.now().isoformat()
        }
        self.answers.append(answer_record)

        # 获取情绪数据
        emotion_stats = get_emotion_stats()
        emotion_record = {
            'round': self.current_round,
            'stats': emotion_stats
        }
        self.emotion_data.append(emotion_record)

        # 立即进入下一轮，异步评分上一轮
        current_round_for_scoring = self.current_round
        current_answer_for_scoring = user_answer
        current_emotion_for_scoring = emotion_stats

        # 检查是否还有下一轮
        if self.current_round < self.max_rounds:
            self.current_round += 1

            # 通知前端：上一轮完成，开始下一轮
            socketio.emit('round_completed', {
                'completed_round': current_round_for_scoring,
                'answer': user_answer,
                'next_round': self.current_round
            }, room=self.session_id)

            # 短暂延迟后开始下一轮
            def next_round_worker():
                time.sleep(1)
                self.start_round(self.current_round)

            threading.Thread(target=next_round_worker, daemon=True).start()

            # 异步评分上一轮
            def async_scoring():
                try:
                    logger.info(f"🔄 开始异步评分第{current_round_for_scoring}轮...")
                    self.score_answer(current_answer_for_scoring, current_emotion_for_scoring,
                                      current_round_for_scoring)
                    logger.info(f"✅ 第{current_round_for_scoring}轮评分完成")
                except Exception as e:
                    logger.error(f"❌ 第{current_round_for_scoring}轮评分失败: {e}")

            threading.Thread(target=async_scoring, daemon=True).start()

        else:
            # 最后一轮，需要等待评分完成
            self.score_answer(current_answer_for_scoring, current_emotion_for_scoring, current_round_for_scoring)
            self.finish_interview()

    def score_answer(self, answer, emotion_stats, round_num=None):
        """评分单轮回答"""
        if round_num is None:
            round_num = self.current_round

        round_key = f"round_{round_num}"

        logger.info(f"📊 开始评分第{round_num}轮...")

        # 流畅度评分
        fluency_score = min(100, max(20, len(answer.split()) * 3))

        # 情绪管理评分
        emotion_score = 60
        if emotion_stats and 'valid_samples' in emotion_stats and emotion_stats['valid_samples'] > 0:
            emotion_score = max(50, min(100, emotion_stats['calm_percentage'] + 30))

        # 知识正确性评分
        knowledge_score = self.evaluate_knowledge(answer, round_num)

        overall_score = (fluency_score + emotion_score + knowledge_score) / 3

        self.scores[round_key] = {
            'fluency': round(fluency_score, 1),
            'emotion': round(emotion_score, 1),
            'knowledge': round(knowledge_score, 1),
            'overall': round(overall_score, 1)
        }

        logger.info(
            f"📊 第{round_num}轮评分完成 - 流畅度:{fluency_score:.1f}, 情绪:{emotion_score:.1f}, 知识:{knowledge_score:.1f}, 总分:{overall_score:.1f}")

        # 发送评分结果到前端
        socketio.emit('score_update', {
            'round': round_num,
            'scores': self.scores[round_key],
            'emotion_stats': emotion_stats
        }, room=self.session_id)

    def evaluate_knowledge(self, answer, round_num):
        """使用AI评估知识正确性"""
        try:
            if not self.chatbot or round_num > len(self.questions):
                return 65

            question = self.questions[round_num - 1]

            evaluation_prompt = f"""请对以下面试回答进行评分（0-100分）：

问题：{question}
回答：{answer}

评分标准：
- 90-100分：回答专业、准确、充分
- 70-89分：回答基本正确，有一定专业性
- 50-69分：回答相关但不够深入
- 30-49分：回答偏离主题或错误较多
- 0-29分：未回答或完全错误

只返回数字分数："""

            response = self.chatbot.send_prompt(evaluation_prompt)

            score = 65
            try:
                import re
                numbers = re.findall(r'\d+', response)
                if numbers:
                    score = int(numbers[0])
                    score = max(0, min(100, score))
                logger.info(f"AI评分: {score}分")
            except:
                logger.warning(f"AI评分解析失败，使用默认分数: {score}")

            return score

        except Exception as e:
            logger.error(f"知识评分失败: {e}")
            return 65

    def finish_interview(self):
        """完成面试"""
        self.interview_completed = True
        logger.info("面试完成，开始生成报告...")

        # 计算总分
        total_scores = {
            'fluency': 0,
            'emotion': 0,
            'knowledge': 0,
            'overall': 0
        }

        for round_scores in self.scores.values():
            for key in total_scores:
                total_scores[key] += round_scores[key]

        for key in total_scores:
            total_scores[key] = total_scores[key] / self.max_rounds

        # 生成面试报告
        report = {
            'session_id': self.session_id,
            'job_position': self.job_position,
            'total_rounds': self.max_rounds,
            'completion_time': datetime.now().isoformat(),
            'scores': total_scores,
            'round_details': self.scores,
            'answers': self.answers,
            'emotion_summary': self.emotion_data
        }

        # 保存报告
        report_path = f"reports/interview_report_{self.session_id}.json"
        os.makedirs('reports', exist_ok=True)
        with open(report_path, 'w', encoding='utf-8') as f:
            json.dump(report, f, ensure_ascii=False, indent=2)

        # 通知前端
        socketio.emit('interview_completed', {
            'message': '面试完成',
            'scores': total_scores,
            'report_available': True
        }, room=self.session_id)

    def cleanup(self):
        """清理资源"""
        self.timer_active = False
        self.recording_active = False
        if self.rtasr_client:
            self.rtasr_client.stop()
        stop_emotion_recording()


# ===== API 接口 =====

@app.route('/api/health')
def health_check():
    """健康检查"""
    return jsonify({
        'status': 'healthy',
        'timestamp': datetime.now().isoformat(),
        'version': '1.0.0'
    })


@app.route('/api/upload-resume', methods=['POST'])
def upload_resume():
    """上传简历"""
    try:
        if 'resume' not in request.files:
            return jsonify({'error': '没有上传文件'}), 400

        file = request.files['resume']
        job_position = request.form.get('job_position', '软件工程师')

        if file.filename == '':
            return jsonify({'error': '文件名为空'}), 400

        session_id = str(uuid.uuid4())
        filename = secure_filename(file.filename)
        file_path = os.path.join(app.config['UPLOAD_FOLDER'], f"{session_id}_{filename}")
        file.save(file_path)

        # OCR提取简历文字
        resume_text = get_resume_text(file_path)

        # 创建面试会话
        session = InterviewSession(session_id, job_position, file_path)
        session.resume_text = resume_text
        interview_sessions[session_id] = session

        return jsonify({
            'session_id': session_id,
            'resume_text': resume_text[:200] + '...' if len(resume_text) > 200 else resume_text,
            'job_position': job_position
        })

    except Exception as e:
        logger.error(f"简历上传错误：{e}")
        return jsonify({'error': str(e)}), 500


@app.route('/api/verify-identity/<session_id>', methods=['POST'])
def verify_identity_api(session_id):
    """身份验证初始化"""
    try:
        if session_id not in interview_sessions:
            return jsonify({'error': '会话不存在'}), 404

        session = interview_sessions[session_id]
        registered_face_path = f"temp/{session_id}_registered.jpg"
        session.registered_face_path = registered_face_path

        try:
            if extract_face_from_resume(session.resume_path, registered_face_path):
                return jsonify({'message': '请对准摄像头进行人脸验证', 'status': 'ready'})
            else:
                return jsonify({'error': '无法从简历中提取人脸，请确保简历中包含清晰的人脸照片'}), 400
        except Exception as e:
            return jsonify({'message': '简历人脸提取失败，但您仍可以继续验证流程', 'status': 'warning'})

    except Exception as e:
        logger.error(f"身份验证初始化错误：{e}")
        return jsonify({'error': str(e)}), 500


@app.route('/api/capture-face/<session_id>', methods=['POST'])
def capture_face_api(session_id):
    """人脸验证"""
    try:
        if session_id not in interview_sessions:
            return jsonify({'error': '会话不存在'}), 404

        session = interview_sessions[session_id]
        data = request.get_json()
        image_data = data.get('image')

        if not image_data:
            return jsonify({'error': '没有接收到图像数据'}), 400

        # 保存捕获的人脸
        if ',' in image_data:
            image_bytes = base64.b64decode(image_data.split(',')[1])
        else:
            image_bytes = base64.b64decode(image_data)

        captured_face_path = f"temp/{session_id}_captured.jpg"
        with open(captured_face_path, 'wb') as f:
            f.write(image_bytes)

        # 人脸比对
        registered_face_path = f"temp/{session_id}_registered.jpg"
        if os.path.exists(registered_face_path):
            similarity_score = verify_identity_web(registered_face_path, captured_face_path)
            verified = similarity_score >= 0.6

            if verified:
                session.is_identity_verified = True
                return jsonify({
                    'verified': True,
                    'similarity_score': float(similarity_score),
                    'message': '身份验证成功'
                })
            else:
                return jsonify({
                    'verified': False,
                    'similarity_score': float(similarity_score),
                    'message': f'身份验证失败，相似度过低'
                })
        else:
            session.is_identity_verified = True
            return jsonify({
                'verified': True,
                'similarity_score': 0.85,
                'message': '身份验证完成'
            })

    except Exception as e:
        logger.error(f"人脸验证错误：{e}")
        return jsonify({'error': str(e)}), 500


@app.route('/api/start-interview/<session_id>', methods=['POST'])
def start_interview_api(session_id):
    """开始面试"""
    try:
        if session_id not in interview_sessions:
            return jsonify({'error': '会话不存在'}), 404

        session = interview_sessions[session_id]

        if not session.is_identity_verified:
            return jsonify({'error': '请先完成身份验证'}), 400

        if session.interview_started:
            logger.warning("面试已经开始，返回当前状态")
            current_question = "面试进行中..."
            if session.questions and session.current_round > 0:
                current_question = session.questions[session.current_round - 1]

            return jsonify({
                'message': '面试已在进行中',
                'total_rounds': session.max_rounds,
                'current_round': session.current_round,
                'current_question': current_question
            })

        # 生成面试题目
        session.generate_questions()

        # 初始化语音识别
        asr_connected = session.init_asr()

        # 开始面试
        session.interview_started = True

        return jsonify({
            'message': '面试开始',
            'total_rounds': session.max_rounds,
            'questions_generated': len(session.questions),
            'asr_connected': asr_connected
        })

    except Exception as e:
        logger.error(f"开始面试错误：{e}")
        return jsonify({'error': str(e)}), 500


@app.route('/api/synthesize-speech', methods=['POST'])
def synthesize_speech():
    """语音合成"""
    try:
        data = request.get_json()
        text = data.get('text', '')

        if not text:
            return jsonify({'success': False, 'message': '没有提供文本'}), 400

        logger.info(f"🔊 语音合成请求: {text[:50]}...")
        success = synthesize_and_play(text)

        audio_path = get_audio_file_path()
        if success and audio_path and os.path.exists(audio_path):
            logger.info(f"✅ 语音文件发送: {audio_path}")
            return send_file(audio_path, mimetype="audio/wav", as_attachment=False)
        else:
            logger.error("❌ 语音合成失败或文件不存在")
            return jsonify({'success': False, 'message': '语音合成失败'}), 500

    except Exception as e:
        logger.error(f"语音合成错误：{e}")
        return jsonify({'success': False, 'message': str(e)}), 500


@app.route('/api/get-recognition-result/<session_id>')
def get_recognition_result(session_id):
    """获取实时语音识别结果"""
    try:
        if session_id not in interview_sessions:
            return jsonify({'error': '会话不存在'}), 404

        session = interview_sessions[session_id]

        # 获取当前识别结果
        current_answer = ""
        if session.rtasr_client and session.rtasr_client.is_connected():
            current_answer = session.rtasr_client.get_result()

        # 获取识别状态
        recognition_status = "未连接"
        if session.rtasr_client:
            if session.rtasr_client.is_connected():
                recognition_status = "正在识别" if session.recording_active else "已连接"
            else:
                recognition_status = "连接失败"

        return jsonify({
            'session_id': session_id,
            'current_answer': current_answer,
            'round': session.current_round,
            'recording_active': session.recording_active,
            'recognition_status': recognition_status,
            'timestamp': datetime.now().isoformat()
        })

    except Exception as e:
        logger.error(f"获取语音识别结果错误: {e}")
        return jsonify({'error': str(e)}), 500


@app.route('/api/download-report/<session_id>')
def download_report(session_id):
    """下载面试报告"""
    try:
        report_path = f"reports/interview_report_{session_id}.json"
        if os.path.exists(report_path):
            return send_file(report_path, as_attachment=True)
        else:
            return jsonify({'error': '报告不存在'}), 404
    except Exception as e:
        return jsonify({'error': str(e)}), 500


# ===== WebSocket 事件 =====

@socketio.on('connect')
def handle_connect():
    logger.info('✅ 客户端连接成功')


@socketio.on('join_session')
def handle_join_session(data):
    """加入面试会话"""
    session_id = data.get('session_id')
    if session_id in interview_sessions:
        join_room(session_id)
        emit('joined_session', {'session_id': session_id})
        logger.info(f"✅ 客户端加入会话: {session_id}")
    else:
        emit('error', {'message': '会话不存在'})
        logger.error(f"❌ 会话不存在: {session_id}")


@socketio.on('start_first_round')
def handle_start_first_round(data):
    """开始第一轮面试"""
    session_id = data.get('session_id')
    if session_id in interview_sessions:
        session = interview_sessions[session_id]
        logger.info(f"🚀 开始第一轮面试: {session_id}")
        session.start_round(1)
    else:
        emit('error', {'message': '会话不存在'})


@socketio.on('skip_question')
def handle_skip_question(data):
    """跳过当前问题"""
    session_id = data.get('session_id')
    if session_id in interview_sessions:
        session = interview_sessions[session_id]
        logger.info(f"⏭️ 跳过问题: {session_id}")
        session.end_answer_timer()


@socketio.on('disconnect')
def handle_disconnect():
    logger.info('❌ 客户端断开连接')


if __name__ == '__main__':
    print("🚀 AI面试系统启动")
    print(f"📊 配置: {TOTAL_ROUNDS}轮面试, 每轮{ANSWER_TIME}秒")
    print("🌐 服务地址: http://localhost:5000")
    print("🔧 运行模式: threading (兼容版)")

    # 确保目录存在
    for dir_name in ['uploads', 'temp', 'reports']:
        os.makedirs(dir_name, exist_ok=True)

    # 🔥 修复：简化启动方式，避免Werkzeug警告
    try:
        # 方式1：尝试SocketIO启动
        socketio.run(
            app,
            debug=False,
            host='0.0.0.0',
            port=5000,
            allow_unsafe_werkzeug=True
        )
    except Exception as e:
        logger.error(f"SocketIO启动失败: {e}")
        print("🔄 尝试标准Flask启动...")
        # 方式2：备用Flask启动
        app.run(debug=False, host='0.0.0.0', port=5000)